package com.hustbaidu.hustmap;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.hustbaidu.hustmap.R.layout;

import android.content.Context;

public class PathManager {
	private static Context context;
	private static List<PathSegmentView> pathList = new ArrayList<PathSegmentView>();
	private static List<NodeInfo> nodeList = new ArrayList<NodeInfo>();
	private static List<Node> openList = new ArrayList<Node>();
	private static List<Node> closeList = new ArrayList<Node>();
	private static PathPointView startPointView;
	private static PathPointView endPointView;
//	private int currentChipX, currentChipY;
//	private int offsetX, offsetY;
	
	public static void setContext(Context context) {
		PathManager.context = context;
	}
	
	public static void addNode(NodeInfo nodeInfo){
		nodeList.add(nodeInfo);
	}
	
	public static void searchPathByPosition(int startX, int startY, int stopX, int stopY){
		
	}

	public static void generatePath(int startId, int endId,  
			int currentChipX, int currentChipY, int currentOffsetX, int currentOffsetY, 
			int mapChipWidth, int mapChipHeight){
		
		pathList.clear();
		PathSegmentView psView = null;

		Node startNode = new Node(nodeList.get(startId).getId(), nodeList.get(startId).getNodePositionX(), nodeList.get(startId).getNodePositionY(), null);
		Node endNode = new Node(nodeList.get(endId).getId(), nodeList.get(endId).getNodePositionX(), nodeList.get(endId).getNodePositionY(), null);
		List<Node> resultList = aStarSearch(startNode, endNode);
	
		for(int i = 0; i < (resultList.size() - 1); i++){
//			System.out.print(resultList.get(i).getId() + "  ");
			psView = new PathSegmentView(context);
			psView.setPositionInfo(resultList.get(i).getX(), resultList.get(i).getY(), 
					resultList.get(i + 1).getX(), resultList.get(i + 1).getY(),
					currentChipX, currentChipY, currentOffsetX, currentOffsetY, mapChipWidth, mapChipHeight);
			pathList.add(psView);
		}
		
		startPointView = new PathPointView(context, R.drawable.pin_green);
		endPointView = new PathPointView(context, R.drawable.pin_red);
		psView = pathList.get(0);
//		System.out.println(psView.getOffsetX() + ":" + psView.getRelativeStartX() + ":" + (startPointView.getWidth() / 2));
		startPointView.setPositionInfo(psView.getOffsetX() + psView.getRelativeStartX() - (startPointView.getWidthProperty() / 2), 
				psView.getOffsetY() + psView.getRelativeStartY() - startPointView.getHeightProperty());
		psView = pathList.get(pathList.size() - 1);
//		System.out.println(psView.getOffsetX() + ":" + psView.getRelativeStopX() + ":" + (endPointView.getWidth() / 2));
		endPointView.setPositionInfo(psView.getOffsetX() + psView.getRelativeStopX() - (endPointView.getWidthProperty() / 2), 
				psView.getOffsetY() + psView.getRelativeStopY() - endPointView.getHeightProperty());

	}
	
	public static void move(int distanceX, int distanceY){
		for(int i = 0; i < pathList.size(); i++){
			pathList.get(i).move(distanceX, distanceY);
		}
		startPointView.move(distanceX, distanceY);
		endPointView.move(distanceX, distanceY);
	}
	
	public static void requestLayout(){
		for(int i = 0; i < pathList.size(); i++){
			pathList.get(i).requestLayout();
		}
	}
	
	public static List<PathSegmentView> getPathList() {
		return pathList;
	}
	
	public static PathPointView getStartPointView() {
		return startPointView;
	}

	public static PathPointView getEndPointView() {
		return endPointView;
	}

	private static List<Node> aStarSearch(Node startNode, Node endNode){
		openList.clear();
		closeList.clear();
		
		List<Node> resultList = new ArrayList<Node>();
		openList.add(startNode);
		Node node = null;
		boolean isFound = false;
		
		while(openList.size() > 0){
			node = openList.get(0);
			if(node.getId() == endNode.getId()){
				isFound = true;
				break;
			}
			
			List<NodeLinkedInfo> linkList = nodeList.get(node.getId()).getNodeLinkedList();
			NodeLinkedInfo nodeLinkedInfo = null;
			for(int i = 0; i < linkList.size(); i++){
				nodeLinkedInfo = linkList.get(i);
				checkPath(nodeLinkedInfo.getId(), 
						nodeList.get(nodeLinkedInfo.getId()).getNodePositionX(), 
						nodeList.get(nodeLinkedInfo.getId()).getNodePositionY(), 
						node, endNode, nodeLinkedInfo.getDistance());
			}
			
			
			closeList.add(openList.remove(0));
			Collections.sort(openList, new NodeFComparator());
			
		}
		
		if(isFound){
			getPath(resultList, node);
		}
		
		return resultList;
	}
	
	private static boolean checkPath(int id, int x, int y, Node parent, Node endNode, int cost){
		Node node = new Node(id, x, y, parent);
		
		if(isListContained(closeList, x, y) != -1){
			return false;
		}
		
		int index = -1;
		if((index = isListContained(openList, x, y)) != -1){
			if((parent.getG() + cost) < openList.get(index).getG()){
				node.setParent(parent);
				countG(node, endNode, cost);
				countF(node);
				openList.set(index, node);
			}
		}
		else{
			 node.setParent(parent);
			 count(node, endNode, cost);
			 openList.add(node);
		}
		
		return true;
	}
	
	private static int isListContained(List<Node> list,int x,int y){
		for(int i = 0; i<list.size(); i++){
			Node node = list.get(i);
			if(node.getX() == x && node.getY() == y){
				return i;
			}        
		}
		return -1;
	}
	
	private static void getPath(List<Node> resultList,Node node){
		if(node.getParent() != null){
			getPath(resultList, node.getParent());
		}
		resultList.add(node);
	}
	
	private static void count(Node node,Node endNode,int cost){
		countG(node, endNode, cost);
		countH(node, endNode);
		countF(endNode);
	}
	
	private static void countG(Node node, Node endNode, int cost){
		if(node.getParent() == null){
			node.setG(cost);
		}
		else{
			node.setG(node.getParent().getG() + cost);
		}
	}
	
	private static void countH(Node node, Node endNode){
		node.setH(Math.abs(node.getX() - endNode.getX()) + Math.abs(node.getY() - endNode.getY()));
	}
	
	private static void countF(Node node){
		node.setF(node.getG()+node.getH());
	}
	
	private static class NodeFComparator implements Comparator<Node>{
		@Override
		public int compare(Node o1, Node o2) {
			return o1.getF()-o2.getF();    
		}
	}
	
	private static class Node {
		private int id;
		private int x;
		private int y;
		private Node parent;
		private int g;
		private int h;
		private int f;
		
		public Node(int id, int x,int y,Node parent){
			this.id = id;
			this.x = x;
			this.y = y;
			this.parent = parent;
		}

		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public int getX() {
			return x;
		}

		public void setX(int x) {
			this.x = x;
		}

		public int getY() {
			return y;
		}

		public void setY(int y) {
			this.y = y;
		}

		public Node getParent() {
			return parent;
		}

		public void setParent(Node parent) {
			this.parent = parent;
		}

		public int getG() {
			return g;
		}

		public void setG(int g) {
			this.g = g;
		}

		public int getH() {
			return h;
		}

		public void setH(int h) {
			this.h = h;
		}

		public int getF() {
			return f;
		}

		public void setF(int f) {
			this.f = f;
		}
		
		
	}

}
